En este notebook se seguiran todos los pasos necesarios para el análisis exploratorio de los datos.
Limpieza de Datos
Comenzaremos por cargar y limpiar la base de datos, asegurandonos que todo se encuentre en la forma más fácil de usar. Es decir todas las variables se deben de encontrar tipificadas propiamente las columnas nombradas de manera que sean fáciles de acceder y los datos en condiciones en las cuáles se pueda realizar inferencia.
import pandas as pdimport numpy as npfrom scipy import stats as statsimport matplotlib.pyplot as pltimport seaborn as snsdata = pd.read_csv('../Data/data_globant.csv')data.head()
Date
Email
Name
Position
Seniority
Location
Studio
Client
Client Tag
Project
Project Tag
Team Name
Engagement
Email Leader
Year
Month
Day
0
02Jan23
natalia.ramirez@tec.globant.com
Natalia Ramírez
Software Developer
Jr
CO/ANT/MED
Engineering
GreenWave Innovations
GWI001
Atlas Initiative
ATLINT
Breaking Badger
3.04
laura.leon@tec.globant.com
2023
1
2
1
03Jan23
natalia.ramirez@tec.globant.com
Natalia Ramírez
Software Developer
Jr
CO/ANT/MED
Engineering
GreenWave Innovations
GWI001
Atlas Initiative
ATLINT
Breaking Badger
2.99
laura.leon@tec.globant.com
2023
1
3
2
04Jan23
natalia.ramirez@tec.globant.com
Natalia Ramírez
Software Developer
Jr
CO/ANT/MED
Engineering
GreenWave Innovations
GWI001
Atlas Initiative
ATLINT
Breaking Badger
2.97
laura.leon@tec.globant.com
2023
1
4
3
05Jan23
natalia.ramirez@tec.globant.com
Natalia Ramírez
Software Developer
Jr
CO/ANT/MED
Engineering
GreenWave Innovations
GWI001
Atlas Initiative
ATLINT
Breaking Badger
2.75
laura.leon@tec.globant.com
2023
1
5
4
06Jan23
natalia.ramirez@tec.globant.com
Natalia Ramírez
Software Developer
Jr
CO/ANT/MED
Engineering
GreenWave Innovations
GWI001
Atlas Initiative
ATLINT
Breaking Badger
3.15
laura.leon@tec.globant.com
2023
1
6
Los datos contienen muchas variables categóricas junto con otras tantas numéricas. En específico las variables que nos dan mayor contexto sobre los datos son aquellas como la fecha en la que se registró(Date), la antigüedad de la persona (Seniority), la posición que ejerce (Position), su equipo (TeamName) y finalmente el engagement que es el valor con el que más importa que trabajemos (Engagement). Ahora sería relevante ver que todas las variables estén nombradas de maneras en las que sean de fácil acceso (sin espacios y en únicamente minúsculas)
data.columns = [col.lower() for col in data.columns]data.columns = [col.replace(' ','_') for col in data.columns]data.head()
date
email
name
position
seniority
location
studio
client
client_tag
project
project_tag
team_name
engagement
email_leader
year
month
day
0
02Jan23
natalia.ramirez@tec.globant.com
Natalia Ramírez
Software Developer
Jr
CO/ANT/MED
Engineering
GreenWave Innovations
GWI001
Atlas Initiative
ATLINT
Breaking Badger
3.04
laura.leon@tec.globant.com
2023
1
2
1
03Jan23
natalia.ramirez@tec.globant.com
Natalia Ramírez
Software Developer
Jr
CO/ANT/MED
Engineering
GreenWave Innovations
GWI001
Atlas Initiative
ATLINT
Breaking Badger
2.99
laura.leon@tec.globant.com
2023
1
3
2
04Jan23
natalia.ramirez@tec.globant.com
Natalia Ramírez
Software Developer
Jr
CO/ANT/MED
Engineering
GreenWave Innovations
GWI001
Atlas Initiative
ATLINT
Breaking Badger
2.97
laura.leon@tec.globant.com
2023
1
4
3
05Jan23
natalia.ramirez@tec.globant.com
Natalia Ramírez
Software Developer
Jr
CO/ANT/MED
Engineering
GreenWave Innovations
GWI001
Atlas Initiative
ATLINT
Breaking Badger
2.75
laura.leon@tec.globant.com
2023
1
5
4
06Jan23
natalia.ramirez@tec.globant.com
Natalia Ramírez
Software Developer
Jr
CO/ANT/MED
Engineering
GreenWave Innovations
GWI001
Atlas Initiative
ATLINT
Breaking Badger
3.15
laura.leon@tec.globant.com
2023
1
6
Con los nombres de las columnas estandarizados verificaremos que todas las columnas estén tipificadas propiamente, es decir que los datos numéricos tengan un tipo de dato numérico y los que no deberían de tenerlo no lo tengan
Todos los datos están tipificados propiamente, entonces entramos a la fase final en la que nos desharemos de columnas innecesarias y redundantes. Para esto es necesario que recordemos el propósito de este proyecto: Intentar predecir cuándo un empleado está por bajar su engagement. Para esto conservaremos solo ciertas cosas - date es la fecha y es altamente relevante para saber cuándo se observó ese engagement - name es el nombre del empleado y servirá para poder identificarlo - position es el puesto que ejercen dentro de la empresa y nos servirá para pruebas estadísticas - seniority es la antigüedad en la empresa y servirá para realizar pruebas estadísticas - location se refiere a la ubicación de la rama en la que trabaja el empleado. Se usará para pruebas estadísticas. - studio es el área en la que trabaja. Se usará para pruebas estadísticas - client se refiere al cliente con el que está trabajando. Se usará para pruebas estadísticas - project es el proyecto en el que trabaja. Se usará para pruebas estadísticas - team_name es el nombre del equipo en el que trabaja. Se usará para pruebas estadísticas - engagement es el engagement que intentaremos predecir, será nuestra variable de respuesta
El resto de las variables se removerán, junto con los registros duplicados.
Con esto terminamos la limpieza de los datos y podemos proceder con el análisis exploratorio.
Análisis Exploratorio
Como parte del análisis exploratorio de estos datos decidimos considerar si realizar pruebas de hipótesis sería una buena idea.
Entendiendo las distribuciones
Para decidir si este tipo de prueba sería una buena idea comenzamos viendo la distribución de los datos de la variable de engagement que será la que intentaremos predecir más adelante.
plt.title('Distribución de valores de engagement')plt.xlabel('Puntaje de engagement')plt.ylabel('Frecuencia')plt.hist(data['engagement'], bins=30, color='purple',density=True);plt.savefig('../Figures/EngagementDist.png')plt.show()
Como se puede notar por su histograma la distribución de estos datos no es precisamente normal ya que hay demasiados valores muy bajos y muy altos (cerca de 0 y cerca de 5) además de que existe un espacio vacío en general entre 1 y 1.5 aproximadamente. Ahora veremos como se distribuye esto si separamos por equipos
n = data['team_name'].nunique()teams =list(data['team_name'].unique())fig, axs = plt.subplots(int(np.ceil(n/2)),2, figsize= (20,20));row =0col =0for team in teams: d = data[data['team_name'] == team] mean = data['engagement'].mean()if col ==2: row +=1 col =0 axs[row, col].hist(d['engagement'],bins=30,color='purple'); axs[row,col].axvline(mean, color='red', linestyle='--', linewidth=2) axs[row, col].set_title(f'Distribución de engagement de {team}') axs[row, col].set_xlabel('Engagement') axs[row, col].set_ylabel('Frecuencia') col +=1fig.suptitle('Distribución de engagement por equipo')plt.savefig('../Figures/EngagementEquipo.png')plt.tight_layout()plt.show()
Con estas gráficas ya creadas se puede notar que el engagement o muy bajo o muy alto no son características de un equipo en especial a lo largo de todo el tiempo en el que se recabaron estos datos. A pesar de esto si es relevante notar que las distribuciones del engagement si cambian dependiendo del equipo del que se trarta.
Ahora sería relevante encontrar si el engagement general baja en alguna fecha, para esto seccionaremos en los 12 meses del año y veremos si alguno presenta una distribución significativamente diferente.
months = [1,2,3,4,5,6]month_names = ['Enero','Febrero','Marzo','Abril','Mayo','Junio']fig, axs = plt.subplots(3,2,figsize=(15,15))row =0col =0for i, month inenumerate(months): d = data[data['date'].dt.month == month] mean = d['engagement'].mean()if col ==2: row +=1 col =0 axs[row,col].hist(d['engagement'], bins =30, color ='purple'); axs[row,col].axvline(mean, color='red', linestyle='--', linewidth=2) axs[row,col].set_title(f'Distribución de engagement en {month_names[i]}') axs[row,col].set_xlabel('Engagement') axs[row,col].set_ylabel('Frecuencia') col +=1fig.suptitle('Distribución de engagement por mes')plt.savefig('../Figures/EgagementMes.png')plt.tight_layout()plt.show()
Las distribuciones no parecen ser significativamente diferentes por mes. Aunque si existe información relevante, como que le mes con el engagement más alto en promedio es Enero donde excede 3, y que de ahí en más el promedio se ve reducido en el resto de los meses manteniendose igual o menor a 3. Esto sugiere que el engagement no se reduce significativamente.
Esto nos muestra que las pruebas de hipótesis no serían el mejor enfoque para poder entender estos datos.
Líneas de Tiempo
Cambiando el enfoque de pruebas de hipótesis, decidimos generar líneas del tiempo segmentando por equipo para ver si sus conductas cambian con el tiempo. Estas gráficas se realizaran por cada uno de los días en los que trabajo cada equipo para evitar valores nulos en la recabación y potencialmente poder agregar esrta información a la base de datos. El engagement de cada equipo se considerará como el promedio del engagement de sus miembros en cualquier fecha dada.
team_engagements = []team_dates = []for team in teams: team_data = data[data['team_name'] == team].sort_values('date') group = team_data.groupby('date', as_index=False)['engagement'].mean() team_engagements.append(group['engagement'].values) team_dates.append(group['date'].values)fig, axs = plt.subplots(5,2,figsize=(20,20))row =0col =0for i inrange(0,10): x = team_dates[i] y = team_engagements[i]if col ==2: row +=1 col =0 axs[row,col].plot(x,y); axs[row,col].set_title(f'Linea del tiempo del engagement de {teams[i]}') axs[row,col].set_xlabel('Fecha') axs[row,col].set_ylabel('Engagement del equipo (promedio)') col +=1fig.suptitle('Linea del tiempo de engagement por equipo')plt.savefig('../Figures/TimelineEquipos.png')plt.tight_layout()plt.show()
Estas gráficas nos proporcionan un poco más de información sobre las distinciones entre los engagements de los equipos, ya que, como se puede notar en la gráfica de Finding Nemo’s Friends, The Great Catsby y Fight Club Penguin a pesar de que los promedios generales y distribuciones del engagement en los meses hayan sido muy similares se puede ver claramente que el performance promedio de estos equipo disminuye a lo largo del tiempo.
Ahora se realizará un análisis similar para una muestra de 10 personas de manera aleatoria para ver si se puede observar alguna tendencia en sus engagements personales.
np.random.seed(42)people = np.random.choice(data['name'].unique(),size =10, replace=False)people_engagements = []people_dates = []for person in people: person_data = data[data['name']==person].sort_values('date') person_data = person_data.groupby('date', as_index =False)['engagement'].mean() people_engagements.append(person_data['engagement'].values) people_dates.append(person_data['date'].values)fig, axs = plt.subplots(5,2,figsize = (20,20))row =0col =0for i inrange(0,10): x = people_dates[i] y = people_engagements[i]if col ==2: row +=1 col =0 axs[row,col].plot(x,y); axs[row,col].set_title(f'Linea del tiempo del engagement de {people[i]}') axs[row,col].set_xlabel('Fecha') axs[row,col].set_ylabel('Engagement') col +=1fig.suptitle('Linea del tiempo de engagement personal')plt.savefig('../Figures/TimelinePersonas.png')plt.tight_layout()plt.show()
Aquí se pueden notar algunos patrones bastante más obvios que muestran que el engagement en efecto decrece con el tiempo para ciertas personas. Esto se puede ver especialmente en los casos de Sofia Delgado y Ana Gómez que pertenecen a los eqipos de Finding Nemo’s Friends y Breaking Badger respectivamente. En ambos casos sus engagements decrecen de manera bastante estable y luego se estancan en un valor alrededor de 3 el cual es el valor promedio del engagement. Adicionalmente se puede notar que varios parecen no seguir mucho un patrón y simplemente oscilar en los valores altos con alguno que otro cero marcando una inasistencia para ese miembro del equipo, un ejemplo de este tipo de conducta se presenta en el caso de Natalia Ramirez y Ana Salas. Finalmente se puede encontrar otro tipo de patrón diferente en el que los valores oscilan en los números altos, luego bajan por un periodo de tiempo y finalmente suben otra vez, esto se puede ver en los casos de Alberto Bravo y Paula Cordero. Estos patrones sugieren que no existe un patrón general para el engagement en todos los empleados y que un modelo sería mejor si intenta predecir los engagements personales.